// 7.1.3 定义类相关的非成员函数
/**
 * 类的作者常常需要定义一些辅助函数，比如add、read和print等。
 * 尽管这些函数定义的操作从概念上来说属于类的接口的组成部分，但它们实际上并不属于类本身。
 * 
 * 一般来说，如果非成员函数是类接口的组成部分，则这些函数的声明应该与类在同一个头文件内.
 */

#include <iostream>
using std::cerr, std::clog;
using std::cin, std::cout, std::endl;

struct Sales_data
{
    // 新成员，关于Sales_data对象的操作
    // 定义在类内部的函数是隐式的inline函数
    std::string isbn() const { return bookNo; } // const成员函数
    //std::string isbn() const { return this->bookNo; } // 同上，但没有必要，因为this是隐式的
    /**
     * 可把isbn常量成员函数想象成如下形式（将this定义为指向常量的常量指针）：
     * std::string Sales_data::isbn(const Sales_data *const this) { return this->bookNo; }
     * 
     * 常量对象，以及常量对象的引用或指针都只能调用常量成员函数。
     */
    Sales_data &combine(const Sales_data &);
    double avg_price() const;

    // 数据成员和2.6.1节相比没有改变
    std::string bookNo;
    unsigned units_sold = 0; // unsigned是unsigned int的缩写
    double revenue = 0.0;
}; // 类必须以分号;结尾

// Sales_data的非成员接口函数
Sales_data add(const Sales_data &, const Sales_data &);
std::ostream &print(std::ostream &, const Sales_data &);
std::istream &read(std::istream &, Sales_data &);

// 类外部定义的成员的名字必须包含它所属的类名
double Sales_data::avg_price() const
{
    if (units_sold)
        return revenue/units_sold;
    else
        return 0;
}

// 定义一个返回this对象的函数
Sales_data& Sales_data::combine(const Sales_data &rhs)
{
    units_sold += rhs.units_sold; // 把rhs的成员加到this对象的成员上
    revenue += rhs.revenue;
    return *this; // 返回调用该函数的对象，记住this是个常量指针，不要直接返回this，要返回this指向的对象
}

// 定义read和print非成员函数
std::istream &read(std::istream &is, Sales_data &item)
{
    double price = 0;
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item)
{
    os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
    return os;
}

// 定义add非成员函数
Sales_data add(const Sales_data &lhs,const Sales_data &rhs)
{
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;
}

int main()
{
    Sales_data total;     // 保存当前求和结果的变量
    if (read(cin, total)) // 读入第一笔交易
    {
        Sales_data trans;        // 保存下一条交易数据的变量
        while (read(cin, trans)) // 读入剩余的交易数据
        {
            if (total.isbn() == trans.isbn()) // 检查isbn
            {
                total.combine(trans); // 更新变量total当前的值
            }
            else
            {
                print(cout, total) << endl; // 输出结果
                total = trans;              // 处理下一本书
            }
        }
        print(cout, total) << endl; // 输出最后一条交易
    }
    else
    {
        cerr << "No data?!" << endl; // 没有输入任何信息，通知用户
    }

    return 0;
}